iT邦幫忙

2024 iThome 鐵人賽

DAY 19
2

這篇主要會講this的綁定,call、apply、bind,以及箭頭函式的this。

this取決於函式的呼叫方式

MDN: The value of this in JavaScript depends on how a function is invoked (runtime binding), not how it is defined.

JavaScript中this的值取決於函式的呼叫方式,而不是它的定義方式。

上篇的範例是函式作為物件的方法呼叫,如果是直接呼叫一般函式呢?此時this指向哪裡?測試看看:

//非嚴格模式
function showName(){
    console.log(this); 
}

showName(); //?

執行結果:
呼叫一般函式
非嚴格模式下:在瀏覽器執行,this指向全域的window物件。

//嚴格模式
"use strict" // -->嚴格模式要加上這句
function showName(){
    console.log(this); 
}

showName();//undefined

在嚴格模式下:一樣在瀏覽器執行,this則是undefined。

this的綁定 (binding)

為什麼this需要這個功能?因為this的值是動態的,會根據函式的呼叫方式有所不同,所以才需要有明確設定this的值,以防this不按預期的結果。

明確綁定:call / apply / bind

這三個方法可以讓我們明確的綁定this值,也就是指定this要指向哪個物件

原先呼叫函式例如test(),而綁定就是在函式名後面加上call / apply / bind,也就是test.call()test.apply()test.bind()這樣。

1. call

語法

call(thisArg, arg1, arg2,..., argN)

thisArg:指定this指向的物件
arg1, arg2,..., argN:傳入該函式的參數

第一個參數放物件,指定this指向此物件:

const fruits = {
  summer:"durian",
  winter:"strawberry"
}

function buyFruits(){
  console.log(this.summer);
}

//沒綁定
buyFruits(); //undefined

//call綁定
buyFruits.call(fruits); //"durian"

沒綁定:直接呼叫buyFruits函式,函式內的this在非嚴格模式下預設指向全域window物件,但在window物件沒有summer這個屬性,所以結果為undefined

call綁定:因為指定this指向fruits物件,所以可以看成console.log(fruits.summer),而物件裡有summer屬性,對應內容而印出"durian"。


假設第一個參數不是放物件,而是字串呢?
範例:

function buyFruits(a,b){
  console.log(this,a,b);
}

buyFruits.call("夏天水果","durian","mango");

執行結果:
第一個參數放字串
有發現嗎?印出來變成String物件了耶~~為什麼啊/images/emoticon/emoticon19.gif

在JavaScript中當我們把基本型別(字串、數值、布林值等)作為this使用時,他們會被包裝成物件,因為this的設計用來指向某物件。

當執行buyFruits.call("夏天水果","durian","mango")時,JavaScript其實是把字串 "夏天水果" 轉換成 new String("夏天水果")物件,並把字串物件作為this傳給函式。
new String


如果都不放參數,會怎麼對應?

function buyFruits(a,b){
  console.log(this,a,b);
}

buyFruits.call(); //window undefined undefined

參數為空,this預設指向全域物件。
所以在瀏覽器中,如果是非嚴格模式,對應全域物件window;在嚴格模式下對應undefined。所以在這this印出window。

2. apply

apply與call差別在於傳參數的方式而已,call傳參數像一般函式,用逗號隔開;apply傳參數會用陣列包起來。

語法:

apply(thisArg, [arg1, arg2,..., argN])

thisArg:指定this指向的物件
[arg1, arg2,..., argN]:以陣列方式傳入該函式的參數

function buyFruits(a,b){
  console.log(this,a,b);
}

buyFruits.apply("夏天水果",["durian","mango"]); //用陣列傳入

執行結果:
apply傳參數會用陣列

3. bind

語法

bind(thisArg, arg1, arg2,..., argN)
  • bind不會修改原始函式,而是建立並回傳一個新函式
  • bind綁定了指定物件,this的值會永久設定該物件。
function getScore(){
  console.log(`${this.name}的分數為${this.score}分`);
}

const studentA = {
    name:"小花",
    score:90
}
const studentB = {
    name:"小白",
    score:88
}

const result = getScore.bind(studentA);
result(); //小花的分數為90分

因為bind會建立並回傳一個新函式,所以需要用一個變數來存放它。
getScore.bind(studentA)表示bind會永久綁定studentA這個物件。

即便再使用call或是apply指定別的物件,他的結果都不會變:

const result = getScore.bind(studentA);
result(); //小花的分數為90分

result.call(studentB); //小花的分數為90分

如果bind過了再bind會怎麼樣?

const result2 = result.bind(studentB);//物件改成studentB
result2(); //小花的分數為90分

測試結果:this指向一樣是指向小花。
也就是bind只能綁定一次,之後又在bind不會有任何影響。

箭頭函式的this

MDN:

Arrow functions differ in their handling of this: they inherit this from the parent scope at the time they are defined.
也就是說父層的this是什麼,箭頭函式的this就是什麼。

However, arrow functions do not have their own this binding.
箭頭函式沒有自己的this綁定。

重點:

  1. 箭頭函式在「建立時」就取得this的值,而不是呼叫時。
  2. 箭頭函式沒有自己的this綁定。

範例:

const obj = {
  name:"test",
  func:function (){
    console.log("func",this);
  },
  arrowFunc:() => {
    console.log("arrowFunc",this)
  }
}
obj.func(); //this指向obj,印出obj內容
obj.arrowFunc(); //指向window

執行結果:
箭頭函式的this

在obj物件中,有兩個屬性,func屬性內容放一般函式,arrowFunc放箭頭函式。
兩者呼叫,可以看到this的指向不同,obj.func()指向到obj,印出obj內容;obj.arrowFunc()則指向到全域window物件。

以上分享~謝謝!

參考資料

MDN - this
Chris 技術筆記-this 決策演算法 - 什麼是 this
What's THIS in JavaScript ? [上][中][下]
JS 原力覺醒 Day17 - this 的四種繫結
淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
JavaScript 忍者秘籍, 2/e (Secrets of the JavaScript Ninja, 2/e)


上一篇
[Day 18] this - 上
下一篇
[Day 20] Prototype 原型
系列文
JavaScript學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言